package com.aletto.demo_android_web3j;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthEstimateGas;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;

public class MarketTestDemo {

    private static final int orderId = 6;

    private static final byte chainId = BigInteger.valueOf(97).byteValue();

    private static final Web3j web3j = Web3j.build(
            new HttpService(
                    "https://endpoints.omniatech.io/v1/bsc/testnet/public",
                    new OkHttpClient.Builder()
                            .connectTimeout(15, TimeUnit.SECONDS)
                            .addNetworkInterceptor(
                                    new HttpLoggingInterceptor()
                                            .setLevel(HttpLoggingInterceptor.Level.BODY)
                            )
                            .build()
            )
    );

    public static void buy(Consumer<String> consumer) {
        Function function = new Function(
                "buy",
                Arrays.asList(
                        new Uint256(100001), // 购买的商品编号
                        new Uint256(Convert.toWei("10", Convert.Unit.ETHER).toBigInteger()), // 商品价格
                        new Uint256(BigInteger.valueOf(1)), // 购买的数量
                        new Uint8(BigInteger.valueOf(1)), // 购买的商品类型：0数字，1实物
                        new Address(C.ACCOUNT.SELLER.address)), // 商家地址
                Collections.emptyList()
        );

        executeRemoteCallTransaction(
                function,
                Credentials.create(C.ACCOUNT.BUYER.privateKey),
                C.MARKET_CONTRACT_ADDRESS,
                consumer
        );
    }

    // 买家发起仲裁
    public static void raiseArbitration(Consumer<String> consumer) {
        Function function = new Function(
                "raiseArbitration",
                Collections.singletonList(
                        new Uint256(orderId) // 订单编号
                ),
                Collections.emptyList()
        );

        executeRemoteCallTransaction(function, Credentials.create(C.ACCOUNT.SELLER.privateKey), C.MARKET_CONTRACT_ADDRESS, consumer);
    }

    // 买家取消仲裁
    public static void cancelArbitration(Consumer<String> consumer) {
        Function function = new Function(
                "cancelArbitration",
                Collections.singletonList(new Uint256(orderId)), // 订单编号
                Collections.emptyList()
        );

        executeRemoteCallTransaction(function, Credentials.create(C.ACCOUNT.BUYER.privateKey), C.MARKET_CONTRACT_ADDRESS, consumer);
    }

    // 商家同意退款
    public static void refund(Consumer<String> consumer) {
        Function function = new Function(
                "refund",
                Collections.singletonList(
                        new Uint256(orderId) // 订单编号
                ),
                Collections.emptyList());

        executeRemoteCallTransaction(function, Credentials.create(C.ACCOUNT.SELLER.privateKey), C.MARKET_CONTRACT_ADDRESS, consumer);
    }

    // 商家取回货款
    public static void takeFund(Consumer<String> consumer) {
        Function function = new Function(
                "takeFund",
                Collections.singletonList(
                        new Uint256(orderId) // 订单编号
                ),
                Collections.emptyList());

        executeRemoteCallTransaction(function, Credentials.create(C.ACCOUNT.SELLER.privateKey), C.MARKET_CONTRACT_ADDRESS, consumer);
    }

    // 客服申请仲裁
    public static void resolveArbitration(Consumer<String> consumer) {
        Function function = new Function(
                "resolveArbitration",
                Arrays.asList(
                        new Uint256(orderId), // 订单编号
                        new Bool(false) // true:商家赢，false：买家赢
                ),
                Collections.emptyList());

        executeRemoteCallTransaction(function, Credentials.create(C.ACCOUNT.SERVICE.privateKey), C.MARKET_CONTRACT_ADDRESS, consumer);
    }

    private static void executeRemoteCallTransaction(Function function, Credentials credentials, String contractAddress, Consumer<String> consumer) {
        try {

            String data = FunctionEncoder.encode(function);

            // nonce
            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST).send();
            BigInteger nonce = ethGetTransactionCount.getTransactionCount();

            // gas
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
            BigInteger gasEstimation = ethGasPrice.getGasPrice();

            // gas limit
            Transaction transaction = Transaction.createEthCallTransaction(credentials.getAddress(), contractAddress, data);
            EthEstimateGas ethEstimateGas = web3j.ethEstimateGas(transaction).send();
            BigInteger amountUsed = ethEstimateGas.getAmountUsed();
            BigInteger gasLimit = BigDecimal.valueOf(amountUsed.longValue()).multiply(new BigDecimal("3")).toBigInteger();

            if (ethEstimateGas.hasError()) {
                consumer.accept("Error estimating gas: " + ethEstimateGas.getError().getMessage());
            } else {
                BigInteger estimatedGas = ethEstimateGas.getAmountUsed();
                consumer.accept("gas: " + estimatedGas.toString() + ", gas Limit" + gasLimit.toString());
            }

            RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasEstimation, gasLimit, contractAddress, data);
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
            EthSendTransaction send = web3j.ethSendRawTransaction(Numeric.toHexString(signedMessage)).send();

            consumer.accept("结果（" + !send.hasError() + "）\n" + send.getTransactionHash());

        } catch (Exception e) {
            e.printStackTrace();
            consumer.accept("ERROR: " + e.getLocalizedMessage());
        }
    }

}
